<template>
  <el-form
    ref="formRef"
    v-bind="$attrs"
    :inline="inline"
    :model="formData"
    :rules="rules"
    :label-width="labelWidth"
    :label-position="labelPosition"
  >
    <el-row v-if="!inline" :gutter="schema.gutter">
      <!-- 遍历字段，生成栅格布局 -->
      <template v-for="(field, key) in schema.properties" :key="key">
        <el-col
          v-if="getIsShow(field.iif)"
          v-bind="getResponsiveLayout(field.layout)"
        >
          <el-form-item :label="field.title" :prop="key.toString()">
            <!-- 动态渲染组件 -->
            <component
              :is="getWidgetComponent(key.toString(), field)"
              v-model="formData[key]"
              @change="onFieldChange(key.toString(), $event)"
              @clear="onFieldClear(key.toString())"
              @blur="onFieldBlur(key.toString())"
              @input="onFieldInput(key.toString(), $event)"
            ></component>
            <component
              :is="field.tips"
              v-if="field.tips"
              style="margin-left: 10px"
            ></component>
          </el-form-item>
        </el-col>
      </template>
    </el-row>
    <template v-for="(field, key) in schema.properties" v-else :key="key">
      <el-form-item
        v-if="getIsShow(field.iif)"
        :label="field.title"
        :prop="key.toString()"
      >
        <!-- 动态渲染组件 -->
        <component
          :is="getWidgetComponent(key.toString(), field)"
          v-model="formData[key]"
          @change="onFieldChange(key.toString(), $event)"
          @clear="onFieldClear(key.toString())"
          @blur="onFieldBlur(key.toString())"
          @input="onFieldInput(key.toString(), $event)"
          @focus="onFieldFocus(key.toString())"
        ></component>
      </el-form-item>
    </template>

    <el-form-item v-if="!hasSlotContent">
      <el-button
        type="primary"
        :icon="sureIcon"
        :loading="loading"
        @click="onSubmit"
      >
        {{ sureText }}
      </el-button>
      <el-button :icon="cancelIcon" @click="onReset">
        {{ cancelText }}
      </el-button>
    </el-form-item>
    <el-form-item v-else>
      <slot name="operate" :on-submit="onSubmit" :on-reset="onReset"></slot>
    </el-form-item>
  </el-form>
</template>

<script lang="ts" setup>
import {
  ElCascader,
  ElCheckbox,
  ElCheckboxGroup,
  ElCol,
  ElColorPicker,
  ElDatePicker,
  ElForm,
  ElFormItem,
  ElInput,
  ElInputNumber,
  ElOption,
  ElRadio,
  ElRadioGroup,
  ElRate,
  ElRow,
  ElSelect,
  ElSlider,
  ElSwitch,
  ElTreeSelect
} from 'element-plus';
import {
  ref,
  h,
  reactive,
  watch,
  computed,
  useAttrs,
  Slots,
  useSlots
} from 'vue';
import { formatPx } from '@fate/utils/formatPx';
import { convertToType } from '@fate/utils/covertValue';
import {
  FFEnum,
  FFLayout,
  FFormProps,
  FFProperty,
  FFRules
} from '@fate/components/fate-form/fform.types';

const slots: Slots = useSlots();
const $attrs = useAttrs();
const formRef = ref<InstanceType<typeof ElForm>>();

const hasSlotContent = !!slots.operate;

const props = withDefaults(defineProps<FFormProps>(), {
  inline: false,
  labelWidth: '80px',
  labelPosition: 'right',
  sureText: '确认',
  cancelText: '重置',
  loading: false
});

const { schema } = props;

// 表单数据
const formData = ref<any>({});

/**
 * 使用计算属性生成动态校验规则
 */
const rules = computed(() => {
  const formRules: { [key: string]: any[] } = {};

  for (const key in schema.properties) {
    const field = schema.properties[key];
    const fieldRules = [] as any[];

    // 必填校验
    if (schema.required?.includes(key)) {
      fieldRules.push({
        required: true,
        message: `${field.title}为必填项`,
        trigger: 'blur'
      });
    }

    // 格式校验
    if (field.format) {
      switch (field.format) {
        case 'email':
          fieldRules.push({
            type: 'email',
            message: '请输入有效的邮箱地址',
            trigger: ['blur', 'change']
          });
          break;
        case 'phone':
          fieldRules.push({
            pattern: /^1[3-9]\d{9}$/,
            message: '请输入有效的手机号',
            trigger: ['blur', 'change']
          });
          break;
        default:
          console.warn(`不支持的格式校验：${field.format}`);
          break;
      }
    }

    // 正则校验
    if (field.pattern) {
      fieldRules.push({
        pattern: new RegExp(field.pattern),
        message: '格式不正确',
        trigger: 'blur'
      });
    }

    // 添加自定义校验规则
    if (field.rules && Array.isArray(field.rules)) {
      field.rules.forEach((customRule: FFRules) => {
        fieldRules.push(customRule);
      });
    }

    formRules[key] = fieldRules;
  }

  return formRules;
});

const options = reactive<{
  [key: string]:
    | any
    | Promise<any[]>
    | any[]
    | Promise<unknown[]>
    | Promise<FFEnum[]>;
}>({});

const $emit = defineEmits([
  'onSubmit',
  'onReset',
  'onChange',
  'onClear',
  'onInput',
  'onFocus',
  'onBlur'
]);

watch(
  () => schema,
  () => {
    loadAllOptions();
    loadDefaultValue();
  },
  { immediate: true, deep: true }
);

/**
 * 加载所有选项数据
 */
async function loadAllOptions() {
  const promises = Object.keys(schema.properties).map((key) =>
    loadOptions(key)
  );
  await Promise.all(promises);
}

/**
 *  加载选项数据
 * @param key
 */
async function loadOptions(key: string) {
  const field = schema.properties[key];
  if (field.enums) {
    options[key] = field.enums;
  }
  if (field?.ui && field.ui.asyncData) {
    options[key] = await field.ui.asyncData();
  }
}

/**
 * 获取默认值
 */
function loadDefaultValue() {
  if (props.model) {
    Object.assign(formData.value, props.model);
  }
  for (const key in schema.properties) {
    const field = schema.properties[key];
    if (field.default !== undefined) {
      formData.value[key] = field.default;
    }
  }
}

/**
 * 动态获取组件
 * @param key 字段key
 * @param field 字段配置
 */
function getWidgetComponent(key: string, field: FFProperty) {
  const widget = field.ui?.widget;
  const props = field.ui?.props;
  const childrenProps = field.ui?.childrenProps ? field.ui?.childrenProps : {};

  switch (widget) {
    case 'checkbox':
      return h(
        ElCheckboxGroup,
        {
          ...props,
          style: {
            width: getComponentWidth(field)
          }
        },
        {
          default: () => {
            if (!options[key]) {
              return null;
            }
            return options[key].map((item: any) => {
              return h(ElCheckbox, {
                ...childrenProps,
                label: item.label,
                value: item.value
              });
            });
          }
        }
      );
    case 'cascader':
      return h(ElCascader, {
        ...props,
        style: {
          width: getComponentWidth(field)
        },
        options: options[key] || []
      });
    case 'color':
      return h(ElColorPicker, {
        ...props,
        style: {
          width: getComponentWidth(field)
        }
      });
    case 'year':
    case 'years':
    case 'month':
    case 'months':
    case 'date':
    case 'dates':
    case 'datetime':
    case 'week':
    case 'datetimerange':
    case 'daterange':
    case 'monthrange':
    case 'yearrange':
      return h(ElDatePicker, {
        type: widget,
        ...props,
        style: {
          width: getComponentWidth(field)
        }
      });
    case 'number':
      return h(ElInputNumber, {
        style: {
          width: getComponentWidth(field)
        }
      });
    case 'radio':
      return h(
        ElRadioGroup,
        {
          ...props,
          style: {
            width: getComponentWidth(field)
          }
        },
        {
          default: () => {
            return options[key].map((item: any) =>
              h(ElRadio, { ...childrenProps }, { item })
            );
          }
        }
      );
    case 'rate':
      return h(ElRate, {
        ...props,
        style: {
          width: getComponentWidth(field)
        }
      });
    case 'select':
      return h(
        ElSelect,
        {
          ...props,
          style: {
            width: getComponentWidth(field)
          }
        },
        {
          default: () => {
            if (!options[key]) {
              return null;
            }
            return options[key].map((item: any) => h(ElOption, item));
          }
        }
      );
    case 'slider':
      return h(ElSlider, {
        ...props,
        style: {
          width: getComponentWidth(field)
        }
      });
    case 'switch':
      return h(ElSwitch, {
        ...props,
        style: {
          width: getComponentWidth(field)
        }
      });
    case 'tree-select':
      return h(ElTreeSelect, {
        ...props,
        style: {
          width: getComponentWidth(field)
        },
        data: options[key]
      });
    case 'upload':
      break;
    default:
      return h(ElInput, {
        ...props,
        style: {
          width: getComponentWidth(field)
        }
      });
  }
}

// 获取响应式布局
function getResponsiveLayout(layout: FFLayout = {}) {
  const globalLayout = props.schema.layout || {};

  // 如果是正常设置span，就跳过响应式
  if (layout.span || globalLayout.span) {
    return {
      span: layout.span || globalLayout.span || 24 // 默认24列
    };
  }

  return {
    xs: layout.xs || globalLayout.xs || 24,
    sm: layout.sm || globalLayout.sm || 12,
    md: layout.md || globalLayout.md || 8,
    lg: layout.lg || globalLayout.lg || 6,
    xl: layout.xl || globalLayout.xl || 4,
    offset: layout.offset || globalLayout.offset || 0
  };
}

/**
 * 字段变化处理
 * @param key 字段key
 * @param value 变化值
 */
function onFieldChange(key: string, value: any) {
  $emit('onChange', { [key]: value });
}

function onFieldInput(key: string, value: any) {
  $emit('onInput', { [key]: value });
}

/**
 *  聚焦事件处理
 * @param key
 */
function onFieldFocus(key: string) {
  $emit('onFocus', key);
}

/**
 *  失去焦点事件处理
 * @param key
 */
function onFieldBlur(key: string) {
  $emit('onBlur', key);
}

/**
 * 清空事件处理
 * @param key
 */
function onFieldClear(key: string) {
  $emit('onClear', key);
}

/**
 * 提交
 */
function onSubmit() {
  formRef.value?.validate((valid: boolean) => {
    if (valid) {
      const covertData = {};
      for (const key in formData.value) {
        const val = formData.value[key];
        const { type } = props.schema.properties[key];
        const covertVal = convertToType(type, val);
        Object.assign(covertData, { [key]: covertVal });
      }

      $emit('onSubmit', covertData);
    }
  });
}

/**
 * 重置
 */
function onReset() {
  formRef.value?.resetFields();
  formData.value = {};
  const covertData = {};
  for (const key in formData.value) {
    const val = formData.value[key];
    const { type } = props.schema.properties[key];
    const covertVal = convertToType(type, val);
    Object.assign(covertData, { [key]: covertVal });
  }

  $emit('onReset', covertData);
}

/**
 * 判断是否显示
 * @param iif
 */
function getIsShow(iif?: boolean) {
  return iif || iif === undefined;
}

/**
 *  获取组件宽度
 * @param field 字段配置
 */
function getComponentWidth(field: FFProperty): string {
  const globalWidth = props.schema.elementWidth;
  const componentWidth = field.elementWidth || globalWidth || '100%';
  return formatPx(componentWidth);
}
</script>

<style lang="scss"></style>
